/*
 * Copyright (C) 2015-2020, Wazuh Inc.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <stdio.h>
#include <string.h>

#include "../../wazuh_modules/wmodules.h"
#include "../../headers/shared.h"
#include "../../wazuh_modules/vulnerability_detector/mocks_wm_vuln_detector.h"

int test_mode = 0;
char **d_sub_strings = NULL;

/* redefinitons/wrapping */

extern int __real_fclose(FILE *__stream);
int __wrap_fclose(FILE *stream) {
    if (test_mode) {
        return mock();
    }
    return __real_fclose(stream);
}

extern size_t __real_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t __wrap_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) {
    if (test_mode) {
        return mock_type(size_t);
    }
    return __real_fwrite(ptr, size, nmemb, stream);
}

extern FILE *__real_fopen(const char * __filename, const char * __modes);
FILE *__wrap_fopen(const char * __filename, const char * __modes) {
    check_expected(__filename);
    check_expected(__modes);
    if (test_mode) {
        return mock_type(FILE *);
    }
    return __real_fopen(__filename, __modes);
}

int __wrap_remove (const char *__filename) {
    if (__filename) check_expected(__filename);
    return mock();
}

extern int __real_fgets(char *__restrict __s, int __n, FILE *__restrict __stream);
int __wrap_fgets (char *__restrict __s, int __n, FILE *__restrict __stream) {
    check_expected(__n);
    if (test_mode) {
        strncpy(__s, mock_type(char *), __n);
        return mock_type(int);
    }
    return __real_fgets(__s,  __n, __stream);
}

char *__wrap_strerror (int __errnum) {
    return mock_type(char*);
}

void __wrap__merror(const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mterror(const char *tag, const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    check_expected(tag);

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mtwarn(const char *tag, const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    check_expected(tag);

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mterror_exit(const char *tag, const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    check_expected(tag);

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mtdebug1(const char *tag, const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    check_expected(tag);

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mtdebug2(const char *tag, const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    check_expected(tag);

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mdebug1(const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

void __wrap__mdebug2(const char * file, int line, const char * func, const char *msg, ...) {
    char formatted_msg[OS_MAXSTR];
    va_list args;

    va_start(args, msg);
    vsnprintf(formatted_msg, OS_MAXSTR, msg, args);
    va_end(args);

    check_expected(formatted_msg);
}

int __wrap_OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS * settings) {
    return mock();
}

int __wrap_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) {
    return mock();
}

int __wrap_StartMQ(const char *key, short int type) {
    return mock();
}

int __wrap_SendMSG(int queue, const char *message, const char *locmsg, char loc) {
    return mock();
}

int __wrap_OS_ConnectUnixDomain(const char *path, int type, int max_msg_size) {
    check_expected(path);
    check_expected(type);
    check_expected(max_msg_size);

    return mock();
}

int __wrap_OS_SendSecureTCP(int sock, uint32_t size, const void * msg) {
    check_expected(sock);
    check_expected(size);
    check_expected(msg);

    return mock();
}

int __wrap_OS_RecvSecureTCP(int sock, char * ret, uint32_t size) {
    check_expected(sock);
    check_expected(size);

    strncpy(ret, mock_type(char*), size);

    return mock();
}

int __wrap_close() {
    return 1;
}

int __wrap_sqlite3_prepare_v2() {
    return mock();
}
int __wrap_wm_vuldet_prepare() {
    return mock();
}

int __wrap_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) {
    *ppDb = mock_type(sqlite3 *);
    return mock();
}

int __wrap_sqlite3_bind_text(sqlite3_stmt* pStmt, int a, const char* b, int c, void *d) {
    check_expected(a);
    if (b) check_expected(b);

    return mock();
}

int __wrap_sqlite3_bind_int(sqlite3_stmt* pStmt, int a, int b) {
    check_expected(a);
    check_expected(b);

    return mock();
}

int __wrap_sqlite3_step() {
    return mock();
}

const unsigned char *__wrap_sqlite3_column_text(sqlite3_stmt *pStmt, int i) {
    check_expected(i);
    return mock_type(const unsigned char*);

}

int __wrap_sqlite3_column_int(sqlite3_stmt *pStmt, int i) {
    check_expected(i);
    return mock();
}

double __wrap_sqlite3_column_double(sqlite3_stmt *pStmt, int i) {
    check_expected(i);
    return mock_type(double);
}

int __wrap_sqlite3_close_v2() {
    return mock();
}

int __wrap_sqlite3_finalize() {
    return mock();
}

int __wrap_wdb_finalize() {
    return mock();
}

const char *__wrap_sqlite3_errmsg(){
    return mock_ptr_type(const char *);
}

cJSON * __wrap_cJSON_CreateObject(void) {
    return mock_type(cJSON *);
}

cJSON * __wrap_cJSON_CreateArray(void) {
    return mock_type(cJSON *);
}

cJSON * __wrap_cJSON_CreateNumber(double num) {
    check_expected(num);
}

cJSON * __wrap_cJSON_CreateString(const char *string) {
    check_expected(string);
}

void __wrap_cJSON_AddItemToArray(cJSON *array, cJSON *item) {
    function_called();
    return;
}

void __wrap_cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) {
    function_called();
    return;
}

cJSON * __wrap_cJSON_GetObjectItem(const cJSON * const object, const char * const string) {
    return mock_type(cJSON *);
}

cJSON* __wrap_cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) {
    if (name) check_expected(name);
    if (string) check_expected(string);
    return mock_type(cJSON *);
}

cJSON * __wrap_cJSON_Parse(const char *value) {
    return mock_type(cJSON *);
}

char * __wrap_cJSON_PrintUnformatted(const cJSON *item) {
    return mock_type(char *);
}

void __wrap_cJSON_Delete(cJSON *item) {
    function_called();
    return;
}

cJSON * __wrap_json_fread(const char * path, char retry) {
    if (path) check_expected(path);
    return mock_type(cJSON *);
}

OSHashNode *__wrap_OSHash_Begin(const OSHash *self, unsigned int *i) {
    return mock_type(OSHashNode *);
}

OSHashNode *__wrap_OSHash_Next(const OSHash *self, unsigned int *i, OSHashNode *current) {
    return mock_type(OSHashNode *);
}

int __wrap_OSHash_Create() {
    return mock();
}

int __wrap_OSHash_setSize(OSHash *self, unsigned int new_size) {
    return mock();
}

void *__wrap_OSHash_Clean(OSHash *self, void (*cleaner)(void*)) {
    return mock_type(void *);
}

int __wrap_OSHash_Add(OSHash *self, const char *key, void *data) {
    if (key) check_expected(key);
    return mock();
}

void *__wrap_OSHash_Get(const OSHash *self, const char *key) {
    if (key) check_expected(key);
    return mock_type(void *);
}

int __wrap_wstr_end(char *str, const char *str_end) {
    if (str) check_expected(str);
    if (str_end) check_expected(str_end);
    return mock();
}

int __wrap_w_uncompress_gzfile(const char *gzfilesrc, const char *gzfiledst) {
    if (gzfilesrc) check_expected(gzfilesrc);
    if (gzfiledst) check_expected(gzfiledst);
    return mock();
}

int __wrap_wurl_request(const char * url, const char * dest, const char *header, const char *data) {
    if (url) check_expected(url);
    if (dest) check_expected(dest);
    if (header) check_expected(header);
    if (data) check_expected(data);
    return mock();
}

unsigned int __wrap_sleep (unsigned int __seconds) {
    return mock_type(unsigned int);
}

int __wrap_OSRegex_Compile(const char *pattern, OSRegex *reg, int flags) {
    if (test_mode) {
        if (pattern) check_expected(pattern);
        return mock();
    }
    return __real_OSRegex_Compile(pattern, reg, flags);
}

const char *__wrap_OSRegex_Execute(const char *str, OSRegex *reg) {
    if (test_mode) {
        if (str) check_expected(str);
        reg->d_sub_strings = d_sub_strings;
        return mock_type(const char *);
    }
    return __real_OSRegex_Execute(str, reg);
}

// wm_vuln_detector mocks

int __wrap_wm_checks_package_vulnerability(char *version_a, const char *operation, const char *version_b, version_type vertype) {
    return mock();
}

int __wrap_wm_vuldet_win_nvd_vulnerabilities(sqlite3 *db, agent_software *agent, wm_vuldet_flags *flags) {
    return mock();
}

int __wrap_wm_vuldet_linux_nvd_vulnerabilities(sqlite3 *db, agent_software *agent, OSHash *cve_table) {
    return mock();
}

int __wrap_OS_ReadXML(const char *file, OS_XML *_lxml) {
    return mock();
}

xml_node **__wrap_OS_GetElementsbyNode(const OS_XML *_lxml, const xml_node *node) {
    return mock_type(xml_node **);
}

char * __wrap_w_get_file_content(const char * path, int max_size) {
    return mock_type(char *);
}

int __wrap_wm_vuldet_json_nvd_parser(char *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    return mock();
}
int __wrap_wm_vuldet_json_wcpe_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    return mock();
}

int __wrap_wm_vuldet_json_msu_parser(cJSON *json_feed, wm_vuldet_db *parsed_vulnerabilities) {
    return mock();
}

int __wrap_opendir() {
    return mock();
}

int __wrap_closedir() {
    return 1;
}

struct dirent * __wrap_readdir() {
    return mock_type(struct dirent *);
}

int __wrap_w_is_file(const char * const file) {
    check_expected(file);
    return mock();
}

// wm_vuln_detector_evr mocks

bool __wrap_pkg_version_relate(const struct pkg_version *a, enum pkg_relation rel, const struct pkg_version *b, version_type vertype) {
    check_expected(rel);
    return mock();
}

bool __wrap_c_isdigit(int c) {
    return mock_type(bool);
}

time_t __wrap_time(time_t *t) {
    return mock_type(time_t);
}

struct tm *__wrap_localtime(const time_t *t) {
    return mock_type(struct tm *);
}

int __wrap_bzip2_uncompress() {
    return mock();
}

//wm_vuln_detector_nvd

int __wrap_wm_vuldet_add_cve_node(cve_vuln_pkg *newPkg, const char *cve, OSHash *cve_table) {
    if (cve_table) {
        cve_vuln_pkg *pkg  = (cve_vuln_pkg *) newPkg;
        cve_vuln_pkg *next = NULL;
        do { // Free each package in the linked list.
            next = pkg->next;
            os_free(pkg->bin_name);
            os_free(pkg->src_name);
            os_free(pkg->arch);
            os_free(pkg->version);

            if (pkg->nvd_cond) {
                os_free(pkg->nvd_cond->operator);
                os_free(pkg->nvd_cond->end_version);
                os_free(pkg->nvd_cond->start_version);
                os_free(pkg->nvd_cond);
            }

            if (pkg->vuln_cond) {
                os_free(pkg->vuln_cond->state);
                os_free(pkg->vuln_cond->operation);
                os_free(pkg->vuln_cond->operation_value);
                os_free(pkg->vuln_cond->condition);
                os_free(pkg->vuln_cond);
            }
            os_free(pkg);

            pkg = next;
        } while (pkg);
    }

    return mock();
}
void __wrap_wm_vuldet_free_cve_node(cve_vuln_pkg *newPkg) {
    if (newPkg) {
        cve_vuln_pkg *pkg  = (cve_vuln_pkg *) newPkg;
        cve_vuln_pkg *next = NULL;
        do { // Free each package in the linked list.
            next = pkg->next;
            os_free(pkg->bin_name);
            os_free(pkg->src_name);
            os_free(pkg->arch);
            os_free(pkg->version);

            if (pkg->nvd_cond) {
                os_free(pkg->nvd_cond->operator);
                os_free(pkg->nvd_cond->end_version);
                os_free(pkg->nvd_cond->start_version);
                os_free(pkg->nvd_cond);
            }

            if (pkg->vuln_cond) {
                os_free(pkg->vuln_cond->state);
                os_free(pkg->vuln_cond->operation);
                os_free(pkg->vuln_cond->operation_value);
                os_free(pkg->vuln_cond->condition);
                os_free(pkg->vuln_cond);
            }
            os_free(pkg);

            pkg = next;
        } while (pkg);
    }
    return;
}
